﻿//This filter is tested and seems to work correctly

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// HSB Color filter
    /// </summary>
    public class AhsbColor : BaseImageFilter
    {
        protected bool m_bClip = false;

        protected int m_nHueShift = 200;
        protected float m_fSaturationShift = 0.5f;
        protected float m_fBrightnessShift = 0.0f;

        /// <summary>
        /// default constructor
        /// </summary>
        public AhsbColor()
            : this(false)
        {
        }

        /// <summary>
        /// primary constructor
        /// </summary>
        /// <param name="_bClip"></param>
        public AhsbColor(bool _bClip)
        {
            m_bClip = _bClip;
        }

        /// <summary>
        /// Use this for cloning instance
        /// </summary>
        /// <param name="_ahsbcolor"></param>
        internal AhsbColor(AhsbColor _ahsbcolor)
        {
            this.m_bClip = _ahsbcolor.m_bClip;
            this.m_nHueShift = _ahsbcolor.m_nHueShift;
        }

        /// <summary>
        /// Hue Shift
        /// </summary>
        public int HueShift
        {
            get { return m_nHueShift; }
            set { m_nHueShift = value; }
        }

        /// <summary>
        /// Gets or sets Brightness Shift
        /// </summary>
        public float BrightnessShift
        {
            get { return m_fBrightnessShift; }
            set { m_fBrightnessShift = value; }
        }

        /// <summary>
        /// Gets or sets saturation shift
        /// </summary>
        public float SaturationShift
        {
            get { return m_fSaturationShift; }
            set { m_fSaturationShift = value; }
        }

        /// <summary>
        /// Sets or gets if color will be clipped
        /// </summary>
        public bool Clip
        {
            get { return m_bClip; }
            set { m_bClip = value; }
        }

        /// <summary>
        /// Executes this filter
        /// </summary>
        /// <param name="_bitmap"></param>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            UnsafeBitmap bitmap = _bitmap;
            Color color = Color.Transparent;
            float hue = 0.0f;
            foreach (int y in Enumerable.Range(0, bitmap.Height))
            {
                foreach (int x in Enumerable.Range(0, bitmap.Width))
                {
                    var pixel = bitmap.GetPixel(x, y);
                    hue = pixel.GetHue();

                    color = ColorFromAhsb(pixel.A, pixel.GetHue() + m_nHueShift, pixel.GetSaturation() + m_fSaturationShift, pixel.GetBrightness()+m_fBrightnessShift);
                    bitmap.SetPixel(x, y, color);
                }
            }
            return _bitmap;
        }

        /// <summary>
        /// Get Color from Ahsb
        /// </summary>
        /// <param name="a"></param>
        /// <param name="h"></param>
        /// <param name="s"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        private Color ColorFromAhsb(int a, float h, float s, float b)
        {
            if (!m_bClip)
            {
                if (0 > a) { a = 0; }
                if (255 < a) { a = 255; }

                if (0f > h) { h = 360f; }
                if (360f < h) { h = 360f; }

                if (0f > s) { s = 0; }
                if (1f < s) { s = 1; }

                if (0f > b) { b = 0.5f; }
                if (1f < b) { b = 1f; }
            }
            else
            {
                if (0 > a) { a = 0; }
                if (255 < a) { a %= 255; }

                if (0f > h) { h %= 360f; }
                if (360f < h) { h %= 360f; }

                if (0f > s) { s = 0; }
                if (1f < s) { s %= 1; }

                if (0f > b) { b = 0.5f; }
                if (1f < b) { b %= 1f; }
            }

            if (0 == s)
            {
                return Color.FromArgb(a, Convert.ToInt32(b * 255),
                  Convert.ToInt32(b * 255), Convert.ToInt32(b * 255));
            }

            float fMax, fMid, fMin;
            int nSextant, iMax, iMid, iMin;

            if (0.5 < b)
            {
                fMax = b - (b * s) + s;
                fMin = b + (b * s) - s;
            }
            else
            {
                fMax = b + (b * s);
                fMin = b - (b * s);
            }

            nSextant = (int)Math.Floor(h / 60f);
            if (300f <= h)
            {
                h -= 360f;
            }
            h /= 60f;
            h -= 2f * (float)Math.Floor(((nSextant + 1f) % 6f) / 2f);
            if (0 == nSextant % 2)
            {
                fMid = h * (fMax - fMin) + fMin;
            }
            else
            {
                fMid = fMin - h * (fMax - fMin);
            }

            iMax = Convert.ToInt32(fMax * 255);
            iMid = Convert.ToInt32(fMid * 255);
            iMin = Convert.ToInt32(fMin * 255);

            switch (nSextant)
            {
                case 1:
                    return Color.FromArgb(a, iMid, iMax, iMin);
                case 2:
                    return Color.FromArgb(a, iMin, iMax, iMid);
                case 3:
                    return Color.FromArgb(a, iMin, iMid, iMax);
                case 4:
                    return Color.FromArgb(a, iMid, iMin, iMax);
                case 5:
                    return Color.FromArgb(a, iMax, iMin, iMid);
                default:
                    return Color.FromArgb(a, iMax, iMid, iMin);
            }

        }

        /// <summary>
        /// used to create a deep copy
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new AhsbColor(this);
        }
    }
}
